package com.lgmshare.component.helper;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.text.TextUtils;

import androidx.annotation.IntDef;
import androidx.annotation.RestrictTo;

import com.lgmshare.component.logger.Logger;
import com.lgmshare.component.utils.AndroidVersionCheckUtils;
import com.lgmshare.component.utils.FileUtils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

/**
 * 多媒体文件处理类
 */
public class MediaHelper {

    public static final String TAG = "MediaHelper";

    /**
     * @hide
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @IntDef({TYPE_IMAGE, TYPE_AUDIO, TYPE_VIDEO, TYPE_OTHER})
    @Retention(RetentionPolicy.SOURCE)
    public @interface MediaFileType {
    }

    public static final int TYPE_IMAGE = 1;
    public static final int TYPE_AUDIO = 2;
    public static final int TYPE_VIDEO = 3;
    public static final int TYPE_OTHER = 4;

    /**************************************************************
     * 使用context.getContentResolver.insert在同一个目录下创建新的uri,
     * 在OutputStream未释放之前不能再开启新的操作,否则会返回Null
     **************************************************************/

    /**
     * @param context
     * @param uri     输入的目标文件
     * @param file    输出的目标文件
     * @description:此方法将公共目录文件保存在沙盒目录下面
     **/
    public static void copyPublicDirToPackageDir(Context context, Uri uri, File file) {
        InputStream is = null;
        OutputStream os = null;
        try {
            ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
            is = new FileInputStream(pfd.getFileDescriptor());
            os = new FileOutputStream(file);
            byte[] buf = new byte[2048];
            int len = 0;
            while ((len = is.read(buf)) != -1) {  // 循环从输入流读取 buffer字节
                os.write(buf, 0, len);        // 将读取的输入流写入到输出流
            }
            os.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            FileUtils.closeIO(os, is);
        }
    }

    /**
     * 图片保存到相册
     *
     * @param context
     * @param bitmap
     * @return
     */
    public static Uri saveImageToDCIM(Context context, Bitmap bitmap) {
        return saveImageToDCIM(context, bitmap, null);
    }

    /**
     * 保存图片到相册（不兼容Android 10）
     *
     * @param context
     * @param bitmap
     * @param fileName
     * @return
     */
    public static Uri saveImageToDCIM(Context context, Bitmap bitmap, String fileName) {
        if (bitmap == null) {
            Logger.e(TAG, "Bitmap is null");
            return null;
        }
        if (TextUtils.isEmpty(fileName)) {
            fileName = getFileName(MediaHelper.TYPE_IMAGE);
        }

        String filePath;
        if (Build.BRAND.equals("Xiaomi")) { // 小米手机
            filePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/" + fileName;
        } else {  // Meizu 、Oppo
            filePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/" + fileName;
        }
        File file = new File(filePath);
        if (file.exists()) {
            file.delete();
        }
        FileOutputStream fos;
        try {
            fos = new FileOutputStream(file);
            // 格式为 JPEG，照相机拍出的图片为JPEG格式的，PNG格式的不能显示在相册中
            if (bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos)) {
                fos.flush();
                fos.close();
                // 插入图库
                MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getAbsolutePath(), fileName, null);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Uri uri = FileUtils.getUri(context, file);
        // 发送广播，通知刷新图库的显示
        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
        return uri;
    }

    /**
     * 图片保存到相册
     *
     * @param context
     * @param bitmap
     * @return
     */
    public static Uri saveImageToMediaStore(Context context, Bitmap bitmap) {
        return saveImageToMediaStore(context, bitmap, null);
    }

    /**
     * 图片保存到相册
     *
     * @param context
     * @param bitmap
     * @param fileName
     * @return
     */
    public static Uri saveImageToMediaStore(Context context, Bitmap bitmap, String fileName) {
        if (bitmap == null) {
            Logger.e(TAG, "Bitmap is null");
            return null;
        }
        if (TextUtils.isEmpty(fileName)) {
            fileName = getFileName(MediaHelper.TYPE_IMAGE);
        }
        OutputStream os = null;
        try {
            Uri uri = getMediaSource(context, null, fileName, TYPE_IMAGE, 0, bitmap.getWidth(), bitmap.getHeight());
            if (uri != null) {
                os = context.getContentResolver().openOutputStream(uri);
            }
            if (os != null) {
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
                os.flush();
                //扫描文件
                if (AndroidVersionCheckUtils.beforeQ()) {
                    scanFile(context, FileUtils.getPath(context, uri), FileUtils.getFileMimeTypeFromUrl(fileName));
                }
            }
            return uri;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            FileUtils.closeIO(os);
        }
        return null;
    }

    /**
     * 图片插入到相册
     *
     * @param context
     * @param sourceFile 本App可以访问到的路径(私有目录)
     */
    public static Uri saveImageToMediaStore(Context context, String sourceFile) {
        return saveImageToMediaStore(context, sourceFile, null);
    }

    /**
     * 图片插入到相册
     *
     * @param context
     * @param sourceFile
     * @param fileName
     */
    public static Uri saveImageToMediaStore(Context context, String sourceFile, String fileName) {
        return saveFileToMediaStore(context, sourceFile, fileName, MediaHelper.TYPE_IMAGE, 0, 0, 0);
    }

    /**
     * 音频插入到媒体库
     *
     * @param context
     * @param sourceFile 本App可以访问到的路径(私有目录)
     */
    public static Uri saveAudioToMediaStore(Context context, String sourceFile, long duration) {
        return saveAudioMediaStore(context, sourceFile, null, duration);
    }

    /**
     * 音频插入到媒体库
     *
     * @param context
     * @param sourceFile
     * @param fileName
     */
    public static Uri saveAudioMediaStore(Context context, String sourceFile, String fileName, long duration) {
        return saveFileToMediaStore(context, sourceFile, fileName, MediaHelper.TYPE_AUDIO, duration, 0, 0);
    }

    /**
     * 视频插入到相册
     *
     * @param context
     * @param sourceFile 视频路径
     * @param duration   视频长度
     */
    public static Uri saveVideoToMediaStore(Context context, String sourceFile, long duration) {
        return saveVideoToMediaStore(context, sourceFile, null, duration);
    }

    /**
     * 保存到视频到本地，并插入MediaStore以保证相册可以查看到,这是更优化的方法，防止读取的视频获取不到宽高
     *
     * @param context    上下文
     * @param sourceFile 文件路径
     * @param fileName   视频文件名
     * @param duration   视频长度 ms
     */
    public static Uri saveVideoToMediaStore(Context context, String sourceFile, String fileName, long duration) {
        return saveFileToMediaStore(context, sourceFile, fileName, MediaHelper.TYPE_VIDEO, duration, 0, 0);
    }

    /**
     * 通过MediaStore保存，兼容AndroidQ，保存成功自动添加到相册数据库，无需再发送广播告诉系统插入相册
     *
     * @param context
     * @param sourceFile 源文件路径（本地私有目录）
     * @param fileName   保存文件的名称
     * @param fileType   保存文件的类型 1：image  2:audio  3:video 4:other
     * @param duration   音视频文件时长
     * @param width      媒体文件长宽
     * @param height
     * @return
     */
    public static Uri saveFileToMediaStore(Context context, final String sourceFile, String fileName, @MediaFileType int fileType, long duration, int width, int height) {
        File file = FileUtils.getFile(sourceFile);
        if (file == null || !file.exists()) {
            Logger.e(TAG, "源文件不存在");
            return null;
        }
        if (TextUtils.isEmpty(fileName)) {
            fileName = getFileName(fileType);
        }
        InputStream is = null;
        OutputStream os = null;
        try {
            Uri uri = getMediaSource(context, file, fileName, fileType, duration, width, height);
            if (uri != null) {
                os = context.getContentResolver().openOutputStream(uri);
            }
            if (os != null) {
                is = new BufferedInputStream(new FileInputStream(sourceFile));
                byte[] buf = new byte[2048];
                int len = 0;
                while ((len = is.read(buf)) != -1) {
                    os.write(buf, 0, len);
                }
                os.flush();

                if (AndroidVersionCheckUtils.beforeQ()) {
                    scanFile(context, FileUtils.getPath(context, uri), FileUtils.getFileMimeTypeFromUrl(fileName));
                }
            }
            return uri;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            FileUtils.closeIO(os, is);
        }
        return null;
    }

    /**
     * @param fileType 文件的类型，1：image  2:audio  3:video 4:other
     * @description:此方法下载文件到公共目录下
     **/
    public static void downloadFileToMediaStore(Context context, final String url, String fileName, @MediaFileType int fileType, final OnFileDownloadListener listener) {
        if (listener != null) {
            listener.onStart();
        }
        final String name;
        if (TextUtils.isEmpty(fileName)) {
            name = getFileName(fileType);
        } else {
            name = fileName;
        }
        Request request = new Request.Builder().url(url).build();
        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(2, TimeUnit.HOURS);
        builder.writeTimeout(2, TimeUnit.HOURS);
        builder.readTimeout(2, TimeUnit.HOURS);
        okHttpClient.newCall(request).enqueue(new Callback() {

            @Override
            public void onFailure(Call call, IOException e) {
                if (listener != null) {
                    listener.onFinish();
                    listener.onFailure(e.getMessage());
                }
            }

            @Override
            public void onResponse(Call call, Response response) {
                //请求取消
                if (call.isCanceled()) {
                    if (listener != null) {
                        listener.onFinish();
                        listener.onCancel();
                    }
                    return;
                }
                InputStream is = null;
                OutputStream os = null;
                try {
                    Uri uri = getMediaSource(context, null, name, fileType, 0, 0, 0);
                    if (uri != null) {
                        os = context.getContentResolver().openOutputStream(uri);
                    } else {
                        if (listener != null) {
                            listener.onFinish();
                            listener.onFailure("创建文件失败");
                        }
                        return;
                    }
                    if (os != null) {
                        is = response.body().byteStream();
                        byte[] buf = new byte[2048];
                        int len = 0;
                        long total = response.body().contentLength();
                        long sum = 0;
                        while ((len = is.read(buf)) != -1) {
                            os.write(buf, 0, len);
                            sum += len;
                            // 下载中
                            if (listener != null) {
                                listener.onProgress(total, sum, len);
                            }
                        }
                        os.flush();
                        // 扫描媒体文件
                        if (AndroidVersionCheckUtils.beforeQ()) {
                            scanFile(context, FileUtils.getPath(context, uri), FileUtils.getFileMimeTypeFromUrl(name));
                        }
                        // 下载完成
                        if (listener != null) {
                            listener.onFinish();
                            listener.onSuccess(uri);
                        }
                    } else {
                        if (listener != null) {
                            listener.onFinish();
                            listener.onFailure("保存文件失败");
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    if (listener != null) {
                        listener.onFinish();
                        listener.onFailure(e.getMessage());
                    }
                } finally {
                    FileUtils.closeIO(os, is);
                }
            }
        });
    }

    private static String getFileName(@MediaFileType int fileType) {
        String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(new Date());
        String fileName;
        switch (fileType) {
            case TYPE_IMAGE:
                fileName = String.format("IMG_%s.jpg", timeStamp);
                break;
            case TYPE_AUDIO:
                fileName = String.format("AUDIO_%s.mp3", timeStamp);
                break;
            case TYPE_VIDEO:
                fileName = String.format("VIDEO_%s.mp4", timeStamp);
                break;
            default:
                fileName = String.format("FILE_%s", timeStamp);
                break;
        }
        return fileName;
    }

    private static Uri getMediaSource(Context context, File file, String fileName, @MediaFileType int fileType, long duration, int width, int height) {
        Uri uri;
        long timeMillis = System.currentTimeMillis();
        ContentValues values = new ContentValues();
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
        values.put(MediaStore.MediaColumns.MIME_TYPE, FileUtils.getFileMimeTypeFromUrl(fileName));
        values.put(MediaStore.MediaColumns.DATE_TAKEN, timeMillis);
        values.put(MediaStore.MediaColumns.DATE_ADDED, timeMillis / 1000);
        values.put(MediaStore.MediaColumns.DATE_MODIFIED, timeMillis / 1000);
        if (FileUtils.isExists(file)) {
            values.put(MediaStore.MediaColumns.SIZE, file.length());
        }
        switch (fileType) {
            case TYPE_IMAGE:
                if (AndroidVersionCheckUtils.hasQ()) {
                    values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
                } else {
                    String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath();
                    FileUtils.createFile(path);
                    values.put(MediaStore.Images.Media.DATA, path + "/" + fileName);
                }
                if (width > 0) values.put(MediaStore.Images.ImageColumns.WIDTH, width);
                if (height > 0) values.put(MediaStore.Images.ImageColumns.HEIGHT, height);
                uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
                break;
            case TYPE_AUDIO:
                if (AndroidVersionCheckUtils.hasQ()) {
                    values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_MUSIC);
                } else {
                    String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getPath();
                    FileUtils.createFile(path);
                    values.put(MediaStore.Images.Media.DATA, path + "/" + fileName);
                }
                if (duration > 0) values.put(MediaStore.Video.VideoColumns.DURATION, duration);
                uri = context.getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
                break;
            case TYPE_VIDEO:
                if (AndroidVersionCheckUtils.hasQ()) {
                    values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES);
                } else {
                    String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getPath();
                    FileUtils.createFile(path);
                    values.put(MediaStore.Images.Media.DATA, path + "/" + fileName);
                }
                if (duration > 0) values.put(MediaStore.Video.VideoColumns.DURATION, duration);
                if (width > 0) values.put(MediaStore.Video.VideoColumns.WIDTH, width);
                if (height > 0) values.put(MediaStore.Video.VideoColumns.HEIGHT, height);
                uri = context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
                break;
            default:
                if (AndroidVersionCheckUtils.hasQ()) {
                    values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
                    uri = context.getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
                } else {
                    String filePath = FileUtils.getExternalFilePath(context, Environment.DIRECTORY_DOWNLOADS, fileName);
                    uri = FileUtils.getUri(context, FileUtils.createFile(filePath));
                }
                break;
        }
        return uri;
    }

    private static void scanFile(Context context, String filePath, String mimeType) {
        MediaScannerConnection.scanFile(context, new String[]{filePath}, new String[]{mimeType}, new MediaScannerConnection.OnScanCompletedListener() {
            @Override
            public void onScanCompleted(String path, Uri uri) {
                Logger.d(TAG, String.format("onScanCompleted#uri:%s   path:%s", uri, path));
            }
        });
    }

    /**
     * @date: 创建时间:2019/12/11
     * @author: gaoxiaoxiong
     * @descripion:文件操作类的监听
     **/
    public interface OnFileDownloadListener {

        /**
         * 请求开始
         */
        void onStart();

        /**
         * 请求结束
         */
        void onFinish();

        /**
         * 请求取消
         */
        void onCancel();

        /**
         * 下载进度
         *
         * @param totalSize   文件大小
         * @param currentSize 下载进度
         * @param offset      下载速度
         */
        void onProgress(long totalSize, long currentSize, long offset);

        /**
         * 下载成功
         */
        void onSuccess(Uri uri);

        /**
         * 下载失败
         */
        void onFailure(String errorMsg);
    }
}
